package io.zhile.research.intellij.ier.common

import io.zhile.research.intellij.ier.common.EvalRecord.reset
import io.zhile.research.intellij.ier.common.Resetter
import io.zhile.research.intellij.ier.common.EvalRecord
import io.zhile.research.intellij.ier.common.LicenseFileRecord
import io.zhile.research.intellij.ier.common.NormalFileRecord
import com.intellij.ide.util.PropertiesComponent
import com.intellij.ide.util.PropertiesComponentImpl
import io.zhile.research.intellij.ier.common.PropertyRecord
import io.zhile.research.intellij.ier.common.PreferenceRecord
import io.zhile.research.intellij.ier.helper.ReflectionHelper
import com.intellij.ide.plugins.IdeaPluginDescriptor
import com.intellij.ide.plugins.PluginManager
import io.zhile.research.intellij.ier.helper.NotificationHelper
import com.intellij.openapi.util.SystemInfo
import com.intellij.ide.Prefs
import java.util.prefs.BackingStoreException
import com.intellij.openapi.application.PathManager
import io.zhile.research.intellij.ier.helper.Constants
import org.jdom.Attribute
import java.io.*
import java.lang.Exception
import java.nio.file.Paths
import java.util.ArrayList
import java.util.prefs.Preferences
import kotlin.Throws
import kotlin.jvm.JvmOverloads

/**
 *
 */
object Resetter {
    private const val DEFAULT_VENDOR = "jetbrains"
    private const val OLD_MACHINE_ID_KEY = "JetBrains.UserIdOnMachine"
    private const val NEW_MACHINE_ID_KEY = DEFAULT_VENDOR + ".user_id_on_machine"
    private const val DEVICE_ID_KEY = DEFAULT_VENDOR + ".device_id"
    private const val EVAL_KEY = "evlsprt"

    /**
     * 放入注册表中的 数据的key, 用来记住用户是否进行了 每次重启时候自动reset
     */
    private val AUTO_RESET_KEY = Constants.PLUGIN_PREFS_PREFIX + ".auto_reset." + Constants.IDE_NAME_LOWER + "." + Constants.IDE_HASH
    @JvmStatic
    val evalRecords: List<EvalRecord>
        get() {
            val list: MutableList<EvalRecord> = ArrayList()
            val evalDir = evalDir
            if (evalDir.exists() && evalDir.isDirectory) {
                evalDir.listFiles { file: File, name: String ->
                    if (name.endsWith(".key")) {
                        list.add(LicenseFileRecord(file))
                    }
                    true
                }
            }
            val licenseDir = licenseDir
            if (licenseDir.exists() && licenseDir.isDirectory) {
                licenseDir.listFiles { file: File, name: String ->
                    if (name.endsWith(".key") || name.endsWith(".license")) {
                        if (file.length() <= 0x400) {
                            list.add(NormalFileRecord(file))
                        }
                    }
                    false
                }
            }
            val state = (PropertiesComponent.getInstance() as PropertiesComponentImpl).state
            if (state != null) {
                var attrName: Attribute
                var attrValue: Attribute
                for (element in state.children) {
                    if (element.name != "property") {
                        continue
                    }
                    attrName = element.getAttribute("name")
                    attrValue = element.getAttribute("value")
                    if (attrName == null || attrValue == null) {
                        continue
                    }
                    if (!attrName.value.startsWith(EVAL_KEY)) {
                        continue
                    }
                    list.add(PropertyRecord(attrName.value))
                }
            }
            val prefsValue = arrayOf(
                    PreferenceRecord(OLD_MACHINE_ID_KEY, true),
                    PreferenceRecord(NEW_MACHINE_ID_KEY),
                    PreferenceRecord(DEVICE_ID_KEY))
            for (record in prefsValue) {
                if (record.value == null) {
                    continue
                }
                list.add(record)
            }
            try {
                val prefsList: MutableList<String> = ArrayList()
                for (name in Preferences.userRoot().node(DEFAULT_VENDOR).childrenNames()) {
                    if (!name.toLowerCase().startsWith(Constants.IDE_NAME_LOWER)) {
                        continue
                    }
                    getAllPrefsKeys(Preferences.userRoot().node(DEFAULT_VENDOR + "/" + name + "/" + Constants.IDE_HASH), prefsList)
                }
                val methodGetProductCode = ReflectionHelper.getMethod(IdeaPluginDescriptor::class.java, "getProductCode")
                if (null != methodGetProductCode) {
                    for (descriptor in PluginManager.getPlugins()) {
                        val productCode = methodGetProductCode.invoke(descriptor) as String
                        if (null == productCode || productCode.isEmpty()) {
                            continue
                        }
                        getAllPrefsKeys(Preferences.userRoot().node(DEFAULT_VENDOR + "/" + productCode.toLowerCase()), prefsList)
                    }
                }
                for (key in prefsList) {
                    if (!key.contains(EVAL_KEY)) {
                        continue
                    }
                    if (key.startsWith("/")) {
                        key = key.substring(1).replace('/', '.')
                    }
                    list.add(PreferenceRecord(key))
                }
            } catch (e: Exception) {
                NotificationHelper.showError(null, "List eval preferences failed!")
            }
            if (SystemInfo.isWindows) {
                for (name in arrayOf("PermanentUserId", "PermanentDeviceId")) {
                    val file = getSharedFile(name)
                    if (null != file && file.exists()) {
                        list.add(NormalFileRecord(file))
                    }
                }
            }
            return list
        }

    fun reset(records: List<EvalRecord>) {
        for (record in records) {
            reset(record)
        }
    }

    fun reset(record: EvalRecord) {
        try {
            record.reset()
        } catch (e: Exception) {
            NotificationHelper.showError(null, e.message)
        }
    }

    @JvmStatic
    var isAutoReset: Boolean
        get() = Prefs.getBoolean(AUTO_RESET_KEY, false)
        set(isAutoReset) {
            Prefs.putBoolean(AUTO_RESET_KEY, isAutoReset)
            syncPrefs()
        }

    @JvmStatic
    fun syncPrefs() {
        try {
            Preferences.userRoot().sync()
        } catch (e: BackingStoreException) {
            NotificationHelper.showError(null, "Flush preferences failed!")
        }
    }

    internal fun getSharedFile(fileName: String?): File? {
        val appData = System.getenv("APPDATA") ?: return null
        return Paths.get(appData, "JetBrains", fileName).toFile()
    }

    internal val evalDir: File
        internal get() {
            val configPath = PathManager.getConfigPath()
            return File(configPath, "eval")
        }
    internal val licenseDir: File
        internal get() = File(PathManager.getConfigPath())

    @Throws(BackingStoreException::class)
    internal fun getAllPrefsKeys(prefs: Preferences, list: MutableList<String>) {
        val childrenNames = prefs.childrenNames()
        if (childrenNames.size == 0) {
            for (key in prefs.keys()) {
                list.add(prefs.absolutePath() + "/" + key)
            }
            return
        }
        for (childName in childrenNames) {
            getAllPrefsKeys(prefs.node(childName), list)
        }
    }
}